/*
 *  cpu_parse_display/cpu_cpuid.c
 *  
 *              CPU Monitor 
 *
 *	 CPUID functionality.
 *
 *    Spring Semester.  044800.    Technion.
 *   Copyright (C) 2006  Evgeny Shulman  and Elizabeth Shtrom 
 */

#include "../cpu_consts.h"
#include "cpu_cpuid.h"

#ifdef __KERNEL__

#include <linux/memory.h>
#include <linux/string.h>
#include <linux/errno.h>

#else
     /* This one for user space */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

#endif



int display_cpuid_features ( int fd );
int display_cpuid_info     ( int fd,  cpuid_data_s *cpu);
int display_cpuid_regs ( 
          unsigned long *eax,
          unsigned long *ebx,
          unsigned long *ecx,
          unsigned long *edx);
          
          
          
          
int indetify_intel       ( cpuid_data_s *cpu );
int	indetify__display_intel_cache (int fd );
int indetify_amd         ( cpuid_data_s *cpu );
int indetify_cache       (int fd, cpuid_data_s *cpu );





const char *generic_cap_flags[] = {
		"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
		"cx8", "apic", NULL, "sep", "mtrr", "pge", "mca", "cmov",
		"pat", "pse36", "psn", "clflsh", NULL, "dtes", "acpi", "mmx",
		"fxsr", "xmm", "xmm2", "selfsnoop", "ht", "acc", "ia64", NULL
	};
	
const char *generic_cap_flags_full[] = { 
	" FPU                               : ",            //0
  " VMode Extensions                  : ",            //1
  " Debugging Extensions              : ",            //2
  " 4-Megabyte Pages                  : ",            //3
  " RDTSC Instruction                 : ",            //4
  " Machine-Specific Registers        : ",            //5
  " Extended Physical Addressing      : ",            //6
  " Machine-Check Exception           : ",            //7
  " CMPXCHG8B Instruction             : ",            //8
  " APIC                              : ",            //9
  NULL                                   ,
  " SYSENTER, SYSEXIT Instructions    : ",            //11
  " Memory Type Range Registers (MTRR): ",            //12
  " Global Pages                      : ",            //13
  " Machine Check Architecture        : ",            //14
  " CMOVcc, FCMOV Instructions        : ",            //15
  " Page Attributes Table             : ",            //16
  " pse36                             : ",            //17
  " psn                               : ",            //18
  " clflsh                            : ",            //19
   NULL                                  ,            //20
  " dtes                              : ",            //21
  " acpi                              : ",            //22
  " MMX                               : ",            //23
  " FXSAVE, FXRSTOR Instructions      : ",            //24
  " SSE                               : ",            //25
  " SSE2                              : ",            //26
	"selfsnoop                          : ",            //27
	"ht                                 : ",            //28
	"acc                                : ",            //29
	"ia64                               : ",            //30
	NULL 
};


/* display cpuid part */




int display_cpuid ( int fd )
{
	unsigned long eax,ebx, ecx,edx;
	cpuid_data_s cpu;
	
	
	PDEBUG_DISP("\n\n---------------CPUID INFO----------------------\n");
	
	
	if ( read_cpuid (fd, 0, &eax,&ebx, &ecx,&edx) )
		 return -1;
	PDEBUG_DISP("\n\nCPUID (eax=0)\n");
	display_cpuid_regs (&eax,&ebx, &ecx,&edx ) ;
	
	if ( read_cpuid (fd, 1,&eax,&ebx, &ecx,&edx) )
		 return -1;
	PDEBUG_DISP("\n\nCPUID (eax=1)\n");
	display_cpuid_regs (&eax,&ebx, &ecx,&edx ) ;
	
	
	if ( read_cpuid (fd, 2,&eax,&ebx, &ecx,&edx) )
		 return -1;
	PDEBUG_DISP("\n\nCPUID (eax=2)\n");
	display_cpuid_regs (&eax,&ebx, &ecx,&edx ) ;
	
	identify (fd,&cpu);
	
	display_cpuid_info(fd, &cpu);
	display_cpuid_features(fd);
	
	return 0;
}







int display_cpuid_regs ( 
          unsigned long *eax,
          unsigned long *ebx,
          unsigned long *ecx,
          unsigned long *edx){
	if ( eax )           	
		PDEBUG_DISP("eax=0x%08lx \n", *eax);
	if ( ebx )           	
		PDEBUG_DISP("ebx=0x%08lx \n", *ebx);
	if ( ecx )           	
		PDEBUG_DISP("ecx=0x%08lx \n", *ecx);
	if ( edx )           	
		PDEBUG_DISP("edx=0x%08lx \n", *edx);	
	return 0;
}

int display_cpuid_info ( int fd,  cpuid_data_s *cpu){
	PDEBUG_DISP ("\n\n CPUID parsed info: \n ================\n") ;
	switch (cpu->vendor) {
		case VENDOR_INTEL:/* Intel */	
			PDEBUG_DISP ("\tVendor:  Intel.\n");
			break;
		case VENDOR_AMD:/* AMD */	
			PDEBUG_DISP ("\tVendor: AMD.\n");
			break;
		default:					
			PDEBUG_DISP ("\tVendor: Unknown vendor\n");
			return -1;
	}
	PDEBUG_DISP ("\tFamily: %d\n\tModel: %d\n\tStepping: %d\n\tType: %d \n\t[%s]\n\n\n",
		cpu->family, cpu->model, cpu->stepping, cpu->type, cpu->name);
		
	if ( cpu->vendor == VENDOR_INTEL) 
		indetify__display_intel_cache(fd);	
		
	return 0 ;
}

int display_cpuid_features ( int fd ){
	int i;
	unsigned long edx;
	PDEBUG_DISP("CPUID (eax=1)  --> processing feautures -->\n");
	
	if ( read_cpuid (fd, 1, NULL, NULL, NULL, &edx) )
		 return -1;
	display_cpuid_regs ( NULL, NULL, NULL, &edx) ;
	
	
	PDEBUG_DISP ("Feature flags:\n");
	for (i = 0; i < 32; i++)
		if (edx & (1 << i))
			PDEBUG_DISP (" %s", generic_cap_flags[i]);
	PDEBUG_DISP ("\n" );
	
	//FULL 
	for (i = 0; i < 32; i++){
		
		if (generic_cap_flags[i] ){
			PDEBUG_DISP (" %s", generic_cap_flags_full[i]);
			if (edx & (1 << i)){
			  PDEBUG_DISP("FOUND\n");
  		}else {
    		PDEBUG_DISP("not found\n");
    	}
  	}
  }

	PDEBUG_DISP ("\n");
	return 0 ;
}


	





/* Intel-specific information */
int identify(int fd , cpuid_data_s *cpu)
{
	char *nameptr;
	unsigned long eax, ebx, ecx, edx;
	int reserved, ret;
	ret= read_cpuid (fd, 0, NULL, &ebx, NULL, NULL); 
	if (ret) {
		fprintf(stderr, "identify:failed to get cpuid 0 for fd=%d ret=%d!\n", fd , ret);
		return -1;
	}
	switch (ebx) {
		case 0x756e6547:/* Intel */	
			cpu->vendor = VENDOR_INTEL;
			break;
		case 0x68747541:/* AMD */	
			cpu->vendor = VENDOR_AMD;
						         break;
		default:					
			return -1;
	}
	

	nameptr = cpu->name;
	/* Family/model/type etc */
	ret=  read_cpuid (fd, 1, &eax, &ebx, &ecx, &edx);
	if (ret) {
		fprintf(stderr, "identify:failed to get cpuid 1 (model)for fd=%d ret=%d!\n", fd , ret);
		return -1;
	}	 
	
	cpu->stepping = eax      & 0xf;
	cpu->model = (eax >> 4)  & 0xf;
	cpu->family= (eax >> 8)  & 0xf;
	cpu->type  = (eax >> 12) & 0x3;
	cpu->brand = (ebx & 0xf);
	reserved = eax >> 14;
	
	
	
	switch (  cpu->vendor ) { 
		case VENDOR_INTEL:
			indetify_intel(cpu);
			break;
		case VENDOR_AMD:
			indetify_amd( cpu );
			break;
	}
		
	return 0;
}


int indetify_intel( cpuid_data_s *cpu ) {
	char *nameptr = cpu->name;
	
	switch (cpu->family) {
			case 5:		/* Family 5 */
				nameptr += sprintf (nameptr, "%s", "Pentium ");
				break;
			case 6:		/* Family 6 */
					break;
	
			case 0xF:	/* Family 15 */
				nameptr += sprintf (nameptr, "%s", "Pentium 4");
				break;
			default:
					nameptr += sprintf (nameptr, "%s", "Intel:Unknown");
					break;
		}
	/* TYPE */
	switch (cpu->type) {
			case 0:
				sprintf (nameptr, "%s", " Original OEM");
				break;
			case 1:
				sprintf (nameptr, "%s", " Overdrive");
				break;
			case 2:
				sprintf (nameptr, "%s", " Dual-capable");
				break;
			case 3:
				sprintf (nameptr, "%s", " Reserved");
				break;
	}/*type*/
	
	return 0;
}

int indetify__display_intel_cache (int fd )
{
	unsigned long eax, ebx, ecx, edx;
	int i,n;

	
	/* Number of times to iterate */
	read_cpuid (fd, 2, &eax, &ebx, &ecx, &edx);
	n = eax & 0xFF;
	PDEBUG_DISP("CACHE: \n=========\n");
	PDEBUG_DISP("CPUID (eax=2) -> iteration number  to do %d\n", n);
	for ( i = 0 ; i < n ; i++ ) {
		read_cpuid (fd, 2, &eax, &ebx, &ecx, &edx);
		PDEBUG_DISP("CPUID (eax=2) iteration number %d\n", i);
		display_cpuid_regs (&eax,&ebx, &ecx,&edx ) ;
	}/*for  count*/
	return 0;
}


int indetify_amd ( cpuid_data_s *cpu )
{
	char *nameptr = cpu->name;

	switch (cpu->family) {
	case 4:
			sprintf (nameptr, "%s", "Am4x/5x ");	break;
	case 5:
		  sprintf (cpu->name, "%s", "K5 K6");	break;
	case 6:
			sprintf (nameptr, "%s", "K7/Athlon/Athlon MP/Duron/Athlon XP");	
			break;
	default:
		sprintf (nameptr, "%s", "AMD:Unknown CPU");
		break;
	}
	return 0 ;
}


